package hasher

import (
	"bufio"
	"crypto/hmac"
	"crypto/md5"
	"encoding/hex"
	"errors"
	"io"
	"os"

	"gitee.com/gousing/helper/internal"
)

// MD5（32位）小写Hex字符串
func Md5(in string) string {
	return hex.EncodeToString(Md5Byte(internal.StringToBytes(in)))
}

// Md5Byte （16位）Hex字节数组
func Md5Byte(in []byte) []byte {
	if len(in) == 0 {
		in = []byte{}
	}
	h := md5.Sum(in)
	return h[:]
}

// Md5File （32位）小写Hex字符串
func Md5File(filePath string) (string, error) {
	data, err := Md5FileBytes(filePath)
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(data), nil
}

// Md5FileBytes （16位）Hex字节数组
func Md5FileBytes(filePath string) ([]byte, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return []byte{}, err
	}
	defer file.Close()
	h := md5.New()
	reader := bufio.NewReader(file)
	buf := make([]byte, 4096) // 4KB的缓冲区
	for {
		n, err := reader.Read(buf)
		if err != nil && err != io.EOF {
			return []byte{}, err
		}
		if n == 0 {
			break
		}
		h.Write(buf[:n])
	}
	return h.Sum(nil), nil
}

// Md5Hmac （32位）小写Hex字符串
func Md5Hmac(in, key string) string {
	return hex.EncodeToString(Md5HmacByte(internal.StringToBytes(in), internal.StringToBytes(key)))
}

// Md5HmacByte （16位）Hex字节数组
func Md5HmacByte(in, key []byte) []byte {
	if len(in) == 0 {
		in = []byte{}
	}
	if len(key) == 0 {
		panic("Md5HmacByte: key is empty")
	}
	hash := hmac.New(md5.New, key)
	hash.Write(in)
	return hash.Sum(nil)
}

// Md5HmacFile （32位）小写Hex字符串
func Md5HmacFile(filePath, key string) (string, error) {
	if len(key) == 0 {
		panic("Md5HmacByte: key is empty")
	}
	data, err := Md5HmacFileBytes(filePath, internal.StringToBytes(key))
	if err != nil {
		return "", err
	}
	return hex.EncodeToString(data), nil
}

// Md5HmacFileBytes （16位）Hex字节数组
func Md5HmacFileBytes(filePath string, key []byte) ([]byte, error) {
	if len(key) == 0 {
		return []byte{}, errors.New("Md5HmacByte: key is empty")
	}
	file, err := os.Open(filePath)
	if err != nil {
		return []byte{}, err
	}
	defer file.Close()
	h := hmac.New(md5.New, key)
	reader := bufio.NewReader(file)
	buf := make([]byte, 4096) // 4KB的缓冲区
	for {
		n, err := reader.Read(buf)
		if err != nil && err != io.EOF {
			return []byte{}, err
		}
		if n == 0 {
			break
		}
		h.Write(buf[:n])
	}
	return h.Sum(nil), nil
}
